psp.o holux.o garmin.o tmpro.o tpg.o \
xcsv.o gcdb.o tiger.o internal_styles.o easygps.o quovadis.o \
gpilots.o saroute.o navicache.o psitrex.o geoniche.o delgpl.o \
- ozi.o nmea.o text.o html.o palmdoc.o netstumbler.o hsa_ndv.o
+ ozi.o nmea.o text.o html.o palmdoc.o netstumbler.o hsa_ndv.o \
+ igc.o
FILTERS=position.o duplicate.o arcdist.o polygon.o smplrout.o reverse_route.o sort.o
snmac - Always use the MAC address as the shortname.
+ IGC
+
+ FAI/IGC Data File -- Used by the international gliding community to
+ record gliding flights. IGC files can be converted to and from tracks
+ representing recorded flights, and routes representing task
+ declarations in other formats.
DATA FILTERS
--- /dev/null
+IGC Data Format Notes.
+======================
+Refer to Appendix 1 of http://www.fai.org:81/gliding/gnss/tech_spec_gnss.asp
+for the specification of the IGC data format.
+
+A sample list of software applications that use data in IGC format can be
+found at http://www.fai.org:81/gliding/gnss/gnss_analysis_software.pdf
+
+GPSBabel can be used to translate data in IGC format to and from various other
+formats.
+
+Routes in other formats are used to represent IGC task declarations.
+Tracks in other formats are used to represent IGC recorded flights.
+
+
+
+Converting to IGC format
+========================
+IGC files generated by GPSBabel will NOT pass security validation tests since
+the data they contain cannot be proven to originate from an approved flight
+recorder. For most software applications that use IGC files this is not an
+issue but for competition scoring, record and badge claims the generated files
+will not be accepted as proof of a flight.
+
+A track stored in another format (GPX for example) representing a recorded
+flight can be converted into an IGC file:
+
+ gpsbabel -i gpx -f mytrk.gpx -o igc -F myflight.igc
+
+If multiple track segments are provided in the input file, the one with the
+most points will be used.
+
+A route stored in another format representing a task declaration can be
+converted into an IGC file:
+
+ gpsbabel -i gpx -f myrte.gpx -o igc -F mytask.igc
+
+A route and a track in other formats can be included into a single IGC file:
+
+ gpsbabel -i gpx -f mytrk.gpx -f myrte.gpx -o igc -F myflight.igc
+
+A similar result can be obtained by downloading the track log and routes
+directly from a GPS device connected to a PC. For example to create an IGC
+file from data recorded in a Garmin GPS connected to the first serial port of
+a PC running Linux:
+
+ gpsbabel -t -r -i garmin -f /dev/ttyS0 -o igc -F myflight.igc
+
+For Windows operating systems:
+
+ gpsbabel -t -r -i garmin -f com1 -o igc -F myflight.igc
+
+A waypoint file in another format containing a waypoint whose short name is
+"PILOT" can be merged into an IGC file. The description field of the waypoint
+will be used for the pilot name in the IGC file header:
+
+ gpsbabel -i gpx -f mytrk.gpx -f myrte.gpx -f mywpt.gpx -o igc -F myflight.igc
+ gpsbabel -w -t -r -i garmin -f /dev/ttyS0 -o igc -F myflight.igc
+
+Some formats such as GPX allow routes, tracks and waypoints to exist in the
+same file and can be used to fully populate an IGC file:
+
+ gpsbabel -i gpx -f myall.gpx -o igc -F myflight.igc
+
+
+
+Converting from IGC format
+==========================
+Data in an IGC file can be converted into other formats. For example to
+generate OziExplorer files containing tracks representing the recorded
+flight (myozi.plt) and routes representing declared tasks (myozi.rte):
+
+ gpsbabel -i igc -f myflight.igc -o ozi -F myozi
+
+Or to GPX format:
+
+ gpsbabel -i igc -f myflight.igc -o gpx -F myflight.gpx
+
+Header information from the IGC file will be written to the description field
+of the track(s).
+
+If both pressure altitude and GNSS altitude are recorded in the IGC file, two
+tracks will be written to the new track file, representing the two altitude
+tracks. The latitude, longitude and timestamps in the tracks will be identical.
+
+A route stored in another format can be merged with an existing IGC file that
+has no task declaration, to generate a new IGC file with a task declaration:
+
+ gpsbabel -i igc -f myflight.igc -i gpx -f myrte.gpx -o igc -F mynew.igc
+
+
+Chris Jones
+Aug 2004
} global_options;
extern global_options global_opts;
+extern const char gpsbabel_version[];
/*
* Extended data if waypoint happens to represent a geocache. This is
void route_add_wpt(route_head *rte, waypoint *wpt);
void route_del_wpt(route_head *rte, waypoint *wpt);
void route_add_head(route_head *rte);
+void route_del_head(route_head *rte);
void route_reverse(const route_head *rte_hd);
void track_add_head(route_head *rte);
+void track_del_head(route_head *rte);
void route_disp_all(route_hdr, route_trl, waypt_cb);
void track_disp_all(route_hdr, route_trl, waypt_cb);
void route_free (route_head *);
#endif /* DEBUG_MEM */
FILE *xfopen(const char *fname, const char *type, const char *errtxt);
+void xfprintf(const char *errtxt, FILE *stream, const char *format, ...);
+void xfputs(const char *errtxt, const char *s, FILE *stream);
int case_ignore_strcmp(const char *s1, const char *s2);
# TODO: Get this list from 'gpsbabel -?' instead of hardcoding it here.
tk_optionMenu $f.ftypes $ftype geo gpsman gpx \
magellan mapsend pcx mapsource gpsutil tiger csv xmap dna psp \
- cetus gpspilot magnav garmin mxf holux ozi tpg
+ cetus gpspilot magnav garmin mxf holux ozi tpg igc
pack $f.lab -side left
pack $f.ent -side left -expand yes -fill x
pack $f.but -side left
--- /dev/null
+/*
+ * FAI/IGC data format translation.
+ *
+ * Refer to Appendix 1 of
+ * http://www.fai.org:81/gliding/gnss/tech_spec_gnss.asp for the
+ * specification of the IGC data format. This translation code was
+ * written when the latest ammendment list for the specification was AL6.
+ *
+ * Copyright (C) 2004 Chris Jones
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111 USA
+ */
+
+#include "defs.h"
+#include <errno.h>
+
+static FILE *file_in;
+static FILE *file_out;
+static char manufacturer[4];
+static const route_head *head;
+static char *timeadj = NULL;
+
+#define MYNAME "IGC"
+#define MAXRECLEN 79 // Includes null terminator and CR/LF
+#define MAXDESCLEN 1024
+#define PRESTRKNAME "PRESALTTRK"
+#define GNSSTRKNAME "GNSSALTTRK"
+#define HDRMAGIC "IGCHDRS"
+#define HDRDELIM "~"
+#define DATEMAGIC "IGCDATE"
+
+/*
+ * IGC record types.
+ * These appear as the first char in each record.
+ */
+typedef enum {
+ rec_manuf_id = 'A', // FR manufacturer and identification
+ rec_fix = 'B', // Fix
+ rec_task = 'C', // Task/declaration
+ rec_diff_gps = 'D', // Differential GPS
+ rec_event = 'E', // Event
+ rec_constel = 'F', // Constellation
+ rec_security = 'G', // Security
+ rec_header = 'H', // File header
+ rec_fix_defn = 'I', // List of extension data included at end of each fix (B) record
+ rec_extn_defn = 'J', // List of data included in each extension (K) record
+ rec_extn_data = 'K', // Extension data
+ rec_log_book = 'L', // Logbook/comments
+
+ // M..Z are spare
+
+ rec_none = 0, // No record
+ rec_bad = 1, // Bad record
+} igc_rec_type_t;
+
+/*
+ * See if two lat/lon pairs are approximately equal.
+ * @param lat1 The latitude of coordinate pair 1
+ * @param lon1 The longitude of coordinate pair 1
+ * @param lat2 The latitude of coordinate pair 2
+ * @param lon2 The longitude of coordinate pair 2
+ * @retval 1 The coordinates are approximately equal
+ * @retval 0 The coordinates are significantly different
+ */
+static unsigned char coords_match(double lat1, double lon1, double lat2, double lon2)
+{
+ return (fabs(lat1 - lat2) < 0.0001 && fabs(lon1 - lon2) < 0.0001) ? 1 : 0;
+}
+
+/*************************************************************************************************
+ * Input file processing
+ */
+
+/*
+ * Get an IGC record from the input file
+ * @param rec Caller allocated storage for the record. At least MAXRECLEN chars must be allocated.
+ * @return the record type. rec_none on EOF, rec_bad on fgets() or parse error.
+ */
+static igc_rec_type_t get_record(char *rec)
+{
+ size_t len;
+
+ if (fgets(rec, MAXRECLEN, file_in) == NULL) {
+ if (feof(file_in)) {
+ return rec_none;
+ } else {
+ warning(MYNAME " fgets(): %s\n", strerror(errno));
+ return rec_bad;
+ }
+ }
+ len = strlen(rec);
+ if (len < 3 || rec[len - 2] != '\r' || rec[len - 1] != '\n' || rec[0] < 'A' || rec[0] > 'Z') {
+ warning(MYNAME " bad input record: '%s'\n", rec);
+ return rec_bad;
+ }
+ rec[len - 2] = '\0';
+ return (igc_rec_type_t) rec[0];
+}
+
+static void rd_init(const char *fname)
+{
+ char ibuf[MAXRECLEN];
+
+ file_in = xfopen(fname, "rb", MYNAME);
+
+ // File must begin with a manufacturer/ID record
+ if (get_record(ibuf) != rec_manuf_id || sscanf(ibuf, "A%3[A-Z]", manufacturer) != 1) {
+ fatal(MYNAME ": %s is not an IGC file\n", fname);
+ }
+}
+
+static void rd_deinit(void)
+{
+ fclose(file_in);
+}
+
+/**
+ * Handle pre- or post-flight task declarations.
+ * A route is created for each set of waypoints in a task declaration.
+ * @param rec A single task record
+ */
+static void igc_task_rec(const char *rec)
+{
+ static char flight_date[7];
+ static unsigned int num_tp, tp_ct;
+ static route_head *rte_head;
+ static time_t creation;
+
+ char task_num[5];
+ char task_desc[MAXRECLEN];
+ waypoint *wpt;
+ unsigned int lat_deg, lat_min, lat_frac;
+ unsigned int lon_deg, lon_min, lon_frac;
+ char lat_hemi[2], lon_hemi[2];
+ char short_name[8];
+ char tmp_str[MAXRECLEN];
+ struct tm tm;
+
+ static enum { id, takeoff, start, turnpoint, finish, landing } state = id;
+
+ // First task record identifies the task to follow
+ if (id == state) {
+ task_desc[0] = '\0';
+ if (sscanf(rec, "C%2u%2u%2u%2u%2u%2u%6[0-9]%4c%2u%[^\r]\r\n",
+ &tm.tm_mday, &tm.tm_mon, &tm.tm_year,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
+ flight_date, task_num, &num_tp, task_desc) < 9) {
+ fatal(MYNAME ": task id (C) record parse error\n'%s'", rec);
+ }
+ task_num[4] = '\0';
+ tm.tm_mon -= 1;
+ if (tm.tm_year < 70) {
+ tm.tm_year += 100;
+ }
+ tm.tm_isdst = 0;
+ creation = mktime(&tm) + get_tz_offset();
+
+ // Create a route to store the task data in.
+ rte_head = route_head_alloc();
+ rte_head->rte_name = xstrdup(task_num);
+ sprintf(tmp_str, DATEMAGIC "%s: %s", flight_date, task_desc);
+ rte_head->rte_desc = xstrdup(tmp_str);
+ route_add_head(rte_head);
+ state++;
+ return;
+ }
+ // Get the waypoint
+ tmp_str[0] = '\0';
+ if (sscanf(rec, "C%2u%2u%3u%1[NS]%3u%2u%3u%1[WE]%[^\r]\r\n",
+ &lat_deg, &lat_min, &lat_frac, lat_hemi,
+ &lon_deg, &lon_min, &lon_frac, lon_hemi, tmp_str) < 8) {
+ fatal(MYNAME ": task waypoint (C) record parse error\n%s", rec);
+ }
+
+ wpt = waypt_new();
+ wpt->latitude = ('N' == lat_hemi[0] ? 1 : -1) *
+ (lat_deg + (lat_min * 1000 + lat_frac) / 1000.0 / 60);
+
+ wpt->longitude = ('E' == lon_hemi[0] ? 1 : -1) *
+ (lon_deg + (lon_min * 1000 + lon_frac) / 1000.0 / 60);
+
+ wpt->creation_time = creation;
+ wpt->description = xstrdup(tmp_str);
+
+ // Name the waypoint according to the order of the task record
+ switch (state) {
+ case takeoff:
+ snprintf(short_name, 8, "TAKEOFF");
+ state++;
+ break;
+
+ case start:
+ snprintf(short_name, 8, "START");
+ tp_ct = 0;
+ state++;
+ break;
+
+ case turnpoint:
+ if (++tp_ct == num_tp) {
+ state++;
+ }
+ snprintf(short_name, 8, "TURN%02u", tp_ct);
+ break;
+
+ case finish:
+ snprintf(short_name, 8, "FINISH");
+ state++;
+ break;
+
+ case landing:
+ snprintf(short_name, 8, "LANDING");
+ state = id;
+ break;
+
+ default:
+ fatal(MYNAME ": task id (C) record internal error\n%s", rec);
+ break;
+ }
+
+ // Zero lat and lon indicates an unknown waypoint
+ if (coords_match(wpt->latitude, wpt->longitude, 0.0, 0.0)) {
+ waypt_free(wpt);
+ return;
+ }
+ wpt->shortname = xstrdup(short_name);
+ route_add_wpt(rte_head, wpt);
+}
+
+static void data_read(void)
+{
+ char ibuf[MAXRECLEN];
+ igc_rec_type_t rec_type;
+ unsigned int hours, mins, secs;
+ unsigned int lat_deg, lat_min, lat_frac;
+ unsigned int lon_deg, lon_min, lon_frac;
+ char lat_hemi[2], lon_hemi[2];
+ char validity;
+ route_head *pres_head = NULL;
+ route_head *gnss_head = NULL;
+ int pres_alt, gnss_alt;
+ char pres_valid = 0;
+ char gnss_valid = 0;
+ waypoint *pres_wpt = NULL;
+ waypoint *gnss_wpt = NULL;
+ time_t date = 0;
+ time_t prev_tod = 0;
+ time_t tod;
+ struct tm tm;
+ char tmp_str[20];
+ char *hdr_data;
+ size_t remain;
+ char trk_desc[MAXDESCLEN + 1];
+
+ strcpy(trk_desc, HDRMAGIC HDRDELIM);
+
+ while (1) {
+ rec_type = get_record(ibuf);
+ switch (rec_type) {
+ case rec_manuf_id:
+ // Manufacturer/ID record already found in rd_init().
+ warning(MYNAME ": duplicate manufacturer/ID record\n");
+ break;
+
+ case rec_header:
+ // Get the header sub type
+ if (sscanf(ibuf, "H%*1[FOP]%3s", tmp_str) != 1) {
+ fatal(MYNAME ": header (H) record parse error\n%s\n%s\n", ibuf, tmp_str);
+ }
+ // Optional long name of record sub type is followed by a
+ // colon. Actual header data follows that.
+ if (NULL == (hdr_data = strchr(ibuf, ':'))) {
+ hdr_data = ibuf + 5;
+ } else {
+ hdr_data++;
+ }
+
+ // Date sub type
+ if (strcmp(tmp_str, "DTE") == 0) {
+ if (sscanf(hdr_data, "%2u%2u%2u", &tm.tm_mday, &tm.tm_mon, &tm.tm_year) != 3) {
+ fatal(MYNAME ": date (H) record parse error\n'%s'\n", ibuf);
+ }
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ tm.tm_mon -= 1;
+ if (tm.tm_year < 70) {
+ tm.tm_year += 100;
+ }
+ tm.tm_isdst = 0;
+ date = mktime(&tm) + get_tz_offset();
+ } else {
+ // Store other header data in the track descriptions
+ if (strlen(trk_desc) < MAXDESCLEN) {
+ strcat(ibuf, HDRDELIM);
+ remain = MAXDESCLEN - strlen(trk_desc);
+ strncat(trk_desc, ibuf, remain);
+ }
+ }
+ break;
+
+ case rec_fix:
+ // Date must appear in file before the first fix record
+ if (date < 1000000L) {
+ fatal(MYNAME ": bad date %ld\n", date);
+ }
+ // Create a track for pressure altitude waypoints
+ if (!pres_head) {
+ pres_head = route_head_alloc();
+ pres_head->rte_name = xstrdup(PRESTRKNAME);
+ pres_head->rte_desc = xstrdup(trk_desc);
+ track_add_head(pres_head);
+ }
+ // Create a second track for GNSS altitude waypoints
+ if (!gnss_head) {
+ gnss_head = route_head_alloc();
+ gnss_head->rte_name = xstrdup(GNSSTRKNAME);
+ gnss_head->rte_desc = xstrdup(trk_desc);
+ track_add_head(gnss_head);
+ }
+ // Create a waypoint from the fix record data
+ if (sscanf(ibuf,
+ "B%2u%2u%2u%2u%2u%3u%1[NS]%3u%2u%3u%1[WE]%c%5d%5d",
+ &hours, &mins, &secs, &lat_deg, &lat_min, &lat_frac,
+ lat_hemi, &lon_deg, &lon_min, &lon_frac, lon_hemi,
+ &validity, &pres_alt, &gnss_alt) != 14) {
+ fatal(MYNAME ": fix (B) record parse error\n%s\n", ibuf);
+ }
+ pres_wpt = waypt_new();
+
+ pres_wpt->latitude = ('N' == lat_hemi[0] ? 1 : -1) *
+ (lat_deg + (lat_min * 1000 + lat_frac) / 1000.0 / 60);
+
+ pres_wpt->longitude = ('E' == lon_hemi[0] ? 1 : -1) *
+ (lon_deg + (lon_min * 1000 + lon_frac) / 1000.0 / 60);
+
+ // Increment date if we pass midnight UTC
+ tod = (hours * 60 + mins) * 60 + secs;
+ if (tod < prev_tod) {
+ date += 24 * 60 * 60;
+ }
+ prev_tod = tod;
+ pres_wpt->creation_time = date + tod;
+
+ // Add the waypoint to the pressure altitude track
+ if (pres_alt) {
+ pres_valid = 1;
+ pres_wpt->altitude = pres_alt;
+ } else {
+ pres_wpt->altitude = unknown_alt;
+ }
+ route_add_wpt(pres_head, pres_wpt);
+
+ // Add the same waypoint with GNSS altitude to the second
+ // track
+ gnss_wpt = waypt_dupe(pres_wpt);
+
+ if (gnss_alt) {
+ gnss_valid = 1;
+ gnss_wpt->altitude = gnss_alt;
+ } else {
+ gnss_wpt->altitude = unknown_alt;
+ }
+ route_add_wpt(gnss_head, gnss_wpt);
+ break;
+
+ case rec_task:
+ // Create a route for each pre-flight declaration
+ igc_task_rec(ibuf);
+ break;
+
+ case rec_log_book:
+ // Get the log book sub type
+ if (sscanf(ibuf, "L%3s", tmp_str) != 1) {
+ fatal(MYNAME ": log book (L) record parse error\n'%s'\n", ibuf);
+ }
+
+ if (strcmp(tmp_str, "PFC") == 0) {
+ // Create a route for each post-flight declaration
+ igc_task_rec(ibuf + 4);
+ break;
+ } else if (global_opts.debug_level) {
+ if (strcmp(tmp_str, "OOI") == 0) {
+ fputs(MYNAME ": Observer Input> ", stdout);
+ } else if (strcmp(tmp_str, "PLT") == 0) {
+ fputs(MYNAME ": Pilot Input> ", stdout);
+ } else if (strcmp(tmp_str, manufacturer) == 0) {
+ fputs(MYNAME ": Manufacturer Input> ", stdout);
+ } else {
+ fputs(MYNAME ": Anonymous Input> ", stdout);
+ fputs(ibuf + 1, stdout);
+ break;
+ }
+ fputs(ibuf + 4, stdout);
+ putchar('\n');
+ }
+ break;
+
+ // These record types are discarded
+ case rec_diff_gps:
+ case rec_event:
+ case rec_constel:
+ case rec_security:
+ case rec_fix_defn:
+ case rec_extn_defn:
+ case rec_extn_data:
+ break;
+
+ // No more records
+ case rec_none:
+
+ // Include pressure altitude track only if it has useful
+ // altitude data or if it is the only track available.
+ if (pres_head && !pres_valid && gnss_head) {
+ track_del_head(pres_head);
+ pres_head = NULL;
+ }
+ // Include GNSS altitude track only if it has useful altitude
+ // data or if it is the only track available.
+ if (gnss_head && !gnss_valid && pres_head) {
+ track_del_head(gnss_head);
+ }
+ return; // All done so bail
+
+ default:
+ case rec_bad:
+ fatal(MYNAME ": failure reading file\n");
+ break;
+ }
+ }
+}
+
+/*************************************************************************************************
+ * Output file processing
+ */
+
+/*************************************************
+ * Callbacks used to scan for specific track types
+ */
+
+static void detect_pres_track(const route_head * rh)
+{
+ if (rh->rte_name && strncmp(rh->rte_name, PRESTRKNAME, 6) == 0) {
+ head = rh;
+ }
+}
+
+static void detect_gnss_track(const route_head * rh)
+{
+ if (rh->rte_name && strncmp(rh->rte_name, GNSSTRKNAME, 6) == 0) {
+ head = rh;
+ }
+}
+
+static void detect_other_track(const route_head * rh)
+{
+ static int max_waypt_ct;
+
+ if (!head) {
+ max_waypt_ct = 0;
+ }
+ // Find other track with the most waypoints
+ if (rh->rte_waypt_ct > max_waypt_ct &&
+ (!rh->rte_name ||
+ (strncmp(rh->rte_name, PRESTRKNAME, 6) != 0 &&
+ strncmp(rh->rte_name, GNSSTRKNAME, 6) != 0))) {
+ head = rh;
+ max_waypt_ct = rh->rte_waypt_ct;
+ }
+}
+
+/*
+ * Identify the pressure altitude and GNSS altitude tracks.
+ * @param pres_track Set by the function to the pressure altitude track
+ * head. NULL if not found.
+ * @param gnss_track Set by the function to the GNSS altitude track
+ * head. NULL if not found.
+ */
+static void get_tracks(const route_head ** pres_track, const route_head ** gnss_track)
+{
+ head = NULL;
+ track_disp_all(detect_pres_track, NULL, NULL);
+ *pres_track = head;
+
+ head = NULL;
+ track_disp_all(detect_gnss_track, NULL, NULL);
+ *gnss_track = head;
+
+ head = NULL;
+ track_disp_all(detect_other_track, NULL, NULL);
+
+ if (!*pres_track && *gnss_track && head) {
+ *pres_track = head;
+ }
+
+ if (!*gnss_track && head) {
+ *gnss_track = head;
+ }
+}
+
+/*************************************************
+ * IGC string formatting functions
+ */
+
+static char *latlon2str(const waypoint * wpt)
+{
+ static char str[18] = "";
+ char lat_hemi = wpt->latitude < 0 ? 'S' : 'N';
+ char lon_hemi = wpt->longitude < 0 ? 'W' : 'E';
+ unsigned char lat_deg = fabs(wpt->latitude);
+ unsigned char lon_deg = fabs(wpt->longitude);
+ unsigned int lat_min = (fabs(wpt->latitude) - lat_deg) * 60000 + 0.5;
+ unsigned int lon_min = (fabs(wpt->longitude) - lon_deg) * 60000 + 0.5;
+
+ if (snprintf(str, 18, "%02u%05u%c%03u%05u%c",
+ lat_deg, lat_min, lat_hemi, lon_deg, lon_min, lon_hemi) != 17) {
+ fatal(MYNAME ": Bad waypoint format '%s'\n", str);
+ }
+ return str;
+}
+
+static char *date2str(struct tm *dt)
+{
+ static char str[7] = "";
+
+ if (snprintf(str, 7, "%02u%02u%02u", dt->tm_mday, dt->tm_mon + 1, dt->tm_year % 100) != 6) {
+ fatal(MYNAME ": Bad date format '%s'\n", str);
+ }
+ return str;
+}
+
+static char *tod2str(struct tm *tod)
+{
+ static char str[7] = "";
+
+ if (snprintf(str, 7, "%02u%02u%02u", tod->tm_hour, tod->tm_min, tod->tm_sec) != 6) {
+ fatal(MYNAME ": Bad time of day format '%s'\n", str);
+ }
+ return str;
+}
+
+/*
+ * Write header records
+ */
+static void wr_header(void)
+{
+ const route_head *pres_track;
+ const route_head *track;
+ struct tm *tm;
+ time_t date;
+ static const char dflt_str[] = "Unknown";
+ const char *str;
+ waypoint *wpt;
+
+ get_tracks(&pres_track, &track);
+ if (!track && pres_track) {
+ track = pres_track;
+ }
+ // Date in header record is that of the first fix record
+ date = !track ? current_time() :
+ ((waypoint *) QUEUE_FIRST(&track->waypoint_list))->creation_time;
+
+ if (NULL == (tm = gmtime(&date))) {
+ fatal(MYNAME ": Bad track timestamp\n");
+ }
+ xfprintf(MYNAME, file_out, "HFDTE%s\r\n", date2str(tm));
+
+ // Other header data may have been stored in track description
+ if (track && track->rte_desc && strncmp(track->rte_desc, HDRMAGIC, strlen(HDRMAGIC)) == 0) {
+ for (str = strtok(track->rte_desc + strlen(HDRMAGIC) + strlen(HDRDELIM), HDRDELIM);
+ str; str = strtok(NULL, HDRDELIM)) {
+ xfprintf(MYNAME, file_out, "%s\r\n", str);
+ }
+ } else {
+ // IGC header info not found so synthesise it.
+ // If a waypoint is supplied with a short name of "PILOT", use
+ // its description as the pilot's name in the header.
+ str = dflt_str;
+ if (NULL != (wpt = find_waypt_by_name("PILOT")) && wpt->description) {
+ str = wpt->description;
+ }
+ xfprintf(MYNAME, file_out, "HFPLTPILOT:%s\r\n", str);
+ }
+}
+
+/*************************************************
+ * Generation of IGC task declaration records
+ */
+
+static void wr_task_wpt_name(const waypoint * wpt, const char *alt_name)
+{
+ xfprintf(MYNAME, file_out, "C%s%s\r\n", latlon2str(wpt),
+ wpt->description ? wpt->description : wpt->shortname ? wpt->shortname : alt_name);
+}
+
+static void wr_task_hdr(const route_head * rte)
+{
+ unsigned char have_takeoff = 0;
+ const waypoint *wpt;
+ char flight_date[7] = "000000";
+ char task_desc[MAXRECLEN] = "";
+ int num_tps = rte->rte_waypt_ct - 2;
+ struct tm *tm;
+ time_t rte_time;
+ static unsigned int task_num = 1;
+
+ if (num_tps < 0) {
+ fatal(MYNAME ": Empty task route\n");
+ }
+ // See if the takeoff and landing waypoints are there or if we need to
+ // generate them.
+ wpt = (waypoint *) QUEUE_LAST(&rte->waypoint_list);
+ if (wpt->shortname && strncmp(wpt->shortname, "LANDING", 6) == 0) {
+ num_tps--;
+ }
+ wpt = (waypoint *) QUEUE_FIRST(&rte->waypoint_list);
+ if (wpt->shortname && strncmp(wpt->shortname, "TAKEOFF", 6) == 0) {
+ have_takeoff = 1;
+ num_tps--;
+ }
+ if (num_tps < 0) {
+ fatal(MYNAME ": Too few waypoints in task route\n");
+ }
+ // Gather data to write to the task identification (first) record
+ rte_time = wpt->creation_time ? wpt->creation_time : current_time();
+ if (NULL == (tm = gmtime(&rte_time))) {
+ fatal(MYNAME ": Bad task route timestamp\n");
+ }
+
+ if (rte->rte_desc) {
+ sscanf(rte->rte_desc, DATEMAGIC "%6[0-9]: %s", flight_date, task_desc);
+ }
+
+ xfprintf(MYNAME, file_out, "C%s%s%s%04u%02u%s\r\n", date2str(tm),
+ tod2str(tm), flight_date, task_num++, num_tps, task_desc);
+
+ if (!have_takeoff) {
+ // Generate the takeoff waypoint
+ wr_task_wpt_name(wpt, "TAKEOFF");
+ }
+}
+
+static void wr_task_wpt(const waypoint * wpt)
+{
+ wr_task_wpt_name(wpt, "");
+}
+
+static void wr_task_tlr(const route_head * rte)
+{
+ // If the landing waypoint is not supplied we need to generate it.
+ const waypoint *wpt = (waypoint *) QUEUE_LAST(&rte->waypoint_list);
+ if (!wpt->shortname || strncmp(wpt->shortname, "LANDIN", 6) != 0) {
+ wr_task_wpt_name(wpt, "LANDING");
+ }
+}
+
+static void wr_tasks(void)
+{
+ route_disp_all(wr_task_hdr, wr_task_tlr, wr_task_wpt);
+}
+
+/*
+ * Write a single fix record
+ */
+static void wr_fix_record(const waypoint * wpt, int pres_alt, int gnss_alt)
+{
+ struct tm *tm;
+
+ if (NULL == (tm = gmtime(&wpt->creation_time))) {
+ fatal(MYNAME ": bad track timestamp\n");
+ }
+
+ if (unknown_alt == pres_alt) {
+ pres_alt = 0;
+ }
+ if (unknown_alt == gnss_alt) {
+ gnss_alt = 0;
+ }
+ xfprintf(MYNAME, file_out, "B%02u%02u%02u%sA%05d%05d\r\n", tm->tm_hour,
+ tm->tm_min, tm->tm_sec, latlon2str(wpt), pres_alt, gnss_alt);
+}
+
+/**
+ * Attempt to align the pressure and GNSS tracks in time.
+ * This is useful when trying to merge a track (lat/lon/time) recorded by a
+ * GPS with a barograph (alt/time) recorded by a seperate instrument with
+ * independent clocks which are not closely synchronised.
+ * @return The number of seconds to add to the GNSS track in order to align
+ * it with the pressure track.
+ */
+static int correlate_tracks(const route_head * pres_track, const route_head * gnss_track)
+{
+ const queue *elem;
+ double last_alt, alt_diff;
+ double speed;
+ time_t pres_time, gnss_time;
+ int time_diff;
+ const waypoint *wpt;
+
+ // Deduce the landing time from the pressure altitude track based on
+ // when we last descended to within 10m of the final track altitude.
+ elem = QUEUE_LAST(&pres_track->waypoint_list);
+ last_alt = ((waypoint *) elem)->altitude;
+ do {
+ elem = elem->prev;
+ if (&pres_track->waypoint_list == elem) {
+ // No track left
+ return 0;
+ }
+ alt_diff = last_alt - ((waypoint *) elem)->altitude;
+ if (alt_diff > 10.0) {
+ // Last part of track was ascending
+ return 0;
+ }
+ } while (alt_diff > -10.0);
+ pres_time = ((waypoint *) elem->next)->creation_time;
+ if (global_opts.debug_level >= 1) {
+ printf(MYNAME ": pressure landing time %s", ctime(&pres_time));
+ }
+ // Deduce the landing time from the GNSS altitude track based on
+ // when the groundspeed last dropped below a certain level.
+ elem = QUEUE_LAST(&gnss_track->waypoint_list);
+ last_alt = ((waypoint *) elem)->altitude;
+ do {
+ wpt = (waypoint *) elem;
+ elem = elem->prev;
+ if (&gnss_track->waypoint_list == elem) {
+ // No track left
+ return 0;
+ }
+ // Get a crude indication of groundspeed from the change in lat/lon
+ time_diff = wpt->creation_time - ((waypoint *) elem)->creation_time;
+ speed = !time_diff ? 0 :
+ (fabs(wpt->latitude - ((waypoint *) elem)->latitude) +
+ fabs(wpt->longitude - ((waypoint *) elem)->longitude)) / time_diff;
+ if (global_opts.debug_level >= 2) {
+ printf(MYNAME ": speed=%f\n", speed);
+ }
+ } while (speed < 0.00003);
+ gnss_time = ((waypoint *) elem->next)->creation_time;
+ if (global_opts.debug_level >= 1) {
+ printf(MYNAME ": gnss landing time %s", ctime(&gnss_time));
+ }
+ // Time adjustment is difference between the two estimated landing times
+ if (15 * 60 < abs(time_diff = pres_time - gnss_time)) {
+ warning(MYNAME ": excessive time adjustment %ds\n", time_diff);
+ }
+ return time_diff;
+}
+
+/**
+ * Interpolate altitude from a track at a given time.
+ * @param track The track containing altitude data.
+ * @param time The time that we are interested in.
+ * @return The altitude interpolated from the track.
+ */
+static double interpolate_alt(const route_head * track, time_t time)
+{
+ static const queue *prev_elem = NULL;
+ static const queue *curr_elem = NULL;
+ const waypoint *prev_wpt;
+ const waypoint *curr_wpt;
+ int time_diff;
+ double alt_diff;
+
+ // Start search at the beginning of the track
+ if (!prev_elem) {
+ curr_elem = prev_elem = QUEUE_FIRST(&track->waypoint_list);
+ }
+ // Find the track points either side of the requested time
+ while (((waypoint *) curr_elem)->creation_time < time) {
+ if (QUEUE_LAST(&track->waypoint_list) == curr_elem) {
+ // Requested time later than all track points, we can't interpolate
+ return unknown_alt;
+ }
+ prev_elem = curr_elem;
+ curr_elem = QUEUE_NEXT(prev_elem);
+ }
+
+ prev_wpt = (waypoint *) prev_elem;
+ curr_wpt = (waypoint *) curr_elem;
+
+ if (QUEUE_FIRST(&track->waypoint_list) == curr_elem) {
+ if (curr_wpt->creation_time == time) {
+ // First point's creation time is an exact match so use it's altitude
+ return curr_wpt->altitude;
+ } else {
+ // Requested time is prior to any track points, we can't interpolate
+ return unknown_alt;
+ }
+ }
+ // Interpolate
+ if (0 == (time_diff = curr_wpt->creation_time - prev_wpt->creation_time)) {
+ // Avoid divide by zero
+ return curr_wpt->altitude;
+ }
+ alt_diff = curr_wpt->altitude - prev_wpt->altitude;
+ return prev_wpt->altitude + (alt_diff / time_diff) * (time - prev_wpt->creation_time);
+}
+
+/*
+ * Pressure altitude and GNSS altitude may be provided in two seperate
+ * tracks. This function attempts to merge them into one.
+ */
+static void wr_track(void)
+{
+ const route_head *pres_track;
+ const route_head *gnss_track;
+ const waypoint *wpt;
+ const queue *elem;
+ const queue *tmp;
+ int time_adj;
+ double pres_alt;
+
+ // Find pressure altitude and GNSS altitude tracks
+ get_tracks(&pres_track, &gnss_track);
+
+ // If both found, attempt to merge them
+ if (pres_track && gnss_track) {
+ if (timeadj) {
+ if (strcmp(timeadj, "auto") == 0) {
+ time_adj = correlate_tracks(pres_track, gnss_track);
+ } else if (sscanf(timeadj, "%d", &time_adj) != 1) {
+ fatal(MYNAME ": bad timeadj argument '%s'\n", timeadj);
+ }
+ } else {
+ time_adj = 0;
+ }
+ if (global_opts.debug_level >= 1) {
+ printf(MYNAME ": adjusting time by %ds\n", time_adj);
+ }
+ // Iterate through waypoints in both tracks simultaneously
+ QUEUE_FOR_EACH(&gnss_track->waypoint_list, elem, tmp) {
+ wpt = (waypoint *) elem;
+ pres_alt = interpolate_alt(pres_track, wpt->creation_time + time_adj);
+ wr_fix_record(wpt, pres_alt, wpt->altitude);
+ }
+ } else {
+ if (pres_track) {
+ // Only the pressure altitude track was found so generate fix
+ // records from it alone.
+ QUEUE_FOR_EACH(&pres_track->waypoint_list, elem, tmp) {
+ wr_fix_record((waypoint *) elem, ((waypoint *) elem)->altitude, unknown_alt);
+ }
+ } else if (gnss_track) {
+ // Only the GNSS altitude track was found so generate fix
+ // records from it alone.
+ QUEUE_FOR_EACH(&gnss_track->waypoint_list, elem, tmp) {
+ wr_fix_record((waypoint *) elem, unknown_alt, ((waypoint *) elem)->altitude);
+ }
+ } else {
+ // No tracks found so nothing to do
+ return;
+ }
+ }
+}
+
+static void wr_init(const char *fname)
+{
+ file_out = xfopen(fname, "wb", MYNAME);
+}
+
+static void wr_deinit(void)
+{
+ fclose(file_out);
+}
+
+static void data_write(void)
+{
+ xfputs(MYNAME, "AXXXZZZGPSBabel\r\n", file_out);
+ wr_header();
+ wr_tasks();
+ wr_track();
+ xfprintf(MYNAME, file_out, "LXXXGenerated by GPSBabel Version %s\r\n", gpsbabel_version);
+ xfputs(MYNAME, "GGPSBabelSecurityRecordGuaranteedToFailVALIChecks\r\n", file_out);
+}
+
+
+static arglist_t igc_args[] = {
+ {"timeadj", &timeadj,
+ "(integer sec or 'auto') Barograph to GPS time diff", ARGTYPE_STRING},
+ {0, 0, 0, 0}
+};
+
+ff_vecs_t igc_vecs = {
+ ff_type_file,
+ rd_init,
+ wr_init,
+ rd_deinit,
+ wr_deinit,
+ data_read,
+ data_write,
+ igc_args
+};
#include <ctype.h>
global_options global_opts;
+const char gpsbabel_version[] = VERSION;
static void
usage(const char *pname, int shorter)
--- /dev/null
+<?xml version="1.0"?>
+<gpx
+ version="1.0"
+creator="GPSBabel - http://gpsbabel.sourceforge.net"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xmlns="http://www.topografix.com/GPX/1/0"
+xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
+<time>2004-08-25T09:57:00Z</time>
+<bounds minlat="-35.498071" minlon ="148.115814" maxlat="-27.755478" maxlon="153.042490" />
+<wpt lat="-29.480116" lon="150.133619">
+ <name>PALLMG</name>
+ <cmt>PALLMG</cmt>
+ <desc>PALLMG</desc>
+ <sym>dot</sym>
+</wpt>
+<wpt lat="-27.755478" lon="149.746093">
+ <name>PILOT</name>
+ <cmt>CHRIS JONES</cmt>
+ <desc>CHRIS JONES</desc>
+ <sym>dot</sym>
+</wpt>
+<wpt lat="-30.342870" lon="150.477059">
+ <name>PLUMTH</name>
+ <cmt>PLUMTHORPE LOC</cmt>
+ <desc>PLUMTHORPE LOC</desc>
+ <sym>dot</sym>
+</wpt>
+<trk>
+<trkseg>
+<trkpt lat="-30.747492" lon="150.720524">
+<ele>0.000000</ele>
+<time>2004-04-24T00:25:32Z</time>
+</trkpt>
+<trkpt lat="-30.747492" lon="150.720529">
+<ele>0.000000</ele>
+<time>2004-04-24T00:25:33Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<trk>
+ <name> #2</name>
+<number>1</number>
+<trkseg>
+<trkpt lat="-30.687127" lon="150.611240">
+<ele>0.000000</ele>
+<time>2004-04-24T02:25:39Z</time>
+</trkpt>
+<trkpt lat="-30.687631" lon="150.609925">
+<ele>0.000000</ele>
+<time>2004-04-24T02:26:10Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<trk>
+ <name> #3</name>
+<number>2</number>
+<trkseg>
+<trkpt lat="-30.676312" lon="150.606406">
+<ele>0.000000</ele>
+<time>2004-04-24T04:00:19Z</time>
+</trkpt>
+<trkpt lat="-30.676907" lon="150.605285">
+<ele>0.000000</ele>
+<time>2004-04-24T04:00:49Z</time>
+</trkpt>
+<trkpt lat="-30.678313" lon="150.605435">
+<ele>0.000000</ele>
+<time>2004-04-24T04:01:19Z</time>
+</trkpt>
+<trkpt lat="-30.677154" lon="150.607184">
+<ele>0.000000</ele>
+<time>2004-04-24T04:01:50Z</time>
+</trkpt>
+<trkpt lat="-30.677127" lon="150.609410">
+<ele>0.000000</ele>
+<time>2004-04-24T04:02:20Z</time>
+</trkpt>
+<trkpt lat="-30.678157" lon="150.607184">
+<ele>0.000000</ele>
+<time>2004-04-24T04:02:51Z</time>
+</trkpt>
+<trkpt lat="-30.675910" lon="150.604497">
+<ele>0.000000</ele>
+<time>2004-04-24T04:03:22Z</time>
+</trkpt>
+<trkpt lat="-30.678672" lon="150.605934">
+<ele>0.000000</ele>
+<time>2004-04-24T04:03:52Z</time>
+</trkpt>
+<trkpt lat="-30.681403" lon="150.607538">
+<ele>0.000000</ele>
+<time>2004-04-24T04:04:23Z</time>
+</trkpt>
+<trkpt lat="-30.677637" lon="150.606975">
+<ele>0.000000</ele>
+<time>2004-04-24T04:04:54Z</time>
+</trkpt>
+<trkpt lat="-30.680180" lon="150.608450">
+<ele>0.000000</ele>
+<time>2004-04-24T04:05:24Z</time>
+</trkpt>
+<trkpt lat="-30.678141" lon="150.606304">
+<ele>0.000000</ele>
+<time>2004-04-24T04:05:55Z</time>
+</trkpt>
+<trkpt lat="-30.678560" lon="150.606814">
+<ele>0.000000</ele>
+<time>2004-04-24T04:06:26Z</time>
+</trkpt>
+<trkpt lat="-30.678098" lon="150.606519">
+<ele>0.000000</ele>
+<time>2004-04-24T04:06:57Z</time>
+</trkpt>
+<trkpt lat="-30.678806" lon="150.606986">
+<ele>0.000000</ele>
+<time>2004-04-24T04:07:28Z</time>
+</trkpt>
+<trkpt lat="-30.677594" lon="150.606095">
+<ele>0.000000</ele>
+<time>2004-04-24T04:07:58Z</time>
+</trkpt>
+<trkpt lat="-30.679751" lon="150.607452">
+<ele>0.000000</ele>
+<time>2004-04-24T04:08:29Z</time>
+</trkpt>
+<trkpt lat="-30.680367" lon="150.607211">
+<ele>0.000000</ele>
+<time>2004-04-24T04:09:00Z</time>
+</trkpt>
+<trkpt lat="-30.679911" lon="150.608922">
+<ele>0.000000</ele>
+<time>2004-04-24T04:09:31Z</time>
+</trkpt>
+<trkpt lat="-30.680147" lon="150.609545">
+<ele>0.000000</ele>
+<time>2004-04-24T04:10:01Z</time>
+</trkpt>
+<trkpt lat="-30.680319" lon="150.607404">
+<ele>0.000000</ele>
+<time>2004-04-24T04:10:32Z</time>
+</trkpt>
+<trkpt lat="-30.679745" lon="150.605071">
+<ele>0.000000</ele>
+<time>2004-04-24T04:11:02Z</time>
+</trkpt>
+<trkpt lat="-30.680266" lon="150.605451">
+<ele>0.000000</ele>
+<time>2004-04-24T04:11:32Z</time>
+</trkpt>
+<trkpt lat="-30.680764" lon="150.606621">
+<ele>0.000000</ele>
+<time>2004-04-24T04:12:03Z</time>
+</trkpt>
+<trkpt lat="-30.680212" lon="150.608380">
+<ele>0.000000</ele>
+<time>2004-04-24T04:12:34Z</time>
+</trkpt>
+<trkpt lat="-30.679332" lon="150.608466">
+<ele>0.000000</ele>
+<time>2004-04-24T04:13:04Z</time>
+</trkpt>
+<trkpt lat="-30.678238" lon="150.605317">
+<ele>0.000000</ele>
+<time>2004-04-24T04:13:35Z</time>
+</trkpt>
+<trkpt lat="-30.675641" lon="150.602539">
+<ele>0.000000</ele>
+<time>2004-04-24T04:14:06Z</time>
+</trkpt>
+<trkpt lat="-30.678248" lon="150.605130">
+<ele>0.000000</ele>
+<time>2004-04-24T04:14:37Z</time>
+</trkpt>
+<trkpt lat="-30.679300" lon="150.607023">
+<ele>0.000000</ele>
+<time>2004-04-24T04:15:07Z</time>
+</trkpt>
+<trkpt lat="-30.679434" lon="150.608160">
+<ele>0.000000</ele>
+<time>2004-04-24T04:15:38Z</time>
+</trkpt>
+<trkpt lat="-30.679214" lon="150.608214">
+<ele>0.000000</ele>
+<time>2004-04-24T04:16:08Z</time>
+</trkpt>
+<trkpt lat="-30.678978" lon="150.608241">
+<ele>0.000000</ele>
+<time>2004-04-24T04:16:39Z</time>
+</trkpt>
+<trkpt lat="-30.679160" lon="150.608788">
+<ele>0.000000</ele>
+<time>2004-04-24T04:17:10Z</time>
+</trkpt>
+<trkpt lat="-30.680314" lon="150.606557">
+<ele>0.000000</ele>
+<time>2004-04-24T04:17:41Z</time>
+</trkpt>
+<trkpt lat="-30.680094" lon="150.605859">
+<ele>0.000000</ele>
+<time>2004-04-24T04:18:11Z</time>
+</trkpt>
+<trkpt lat="-30.676156" lon="150.605741">
+<ele>0.000000</ele>
+<time>2004-04-24T04:18:41Z</time>
+</trkpt>
+<trkpt lat="-30.675427" lon="150.604212">
+<ele>0.000000</ele>
+<time>2004-04-24T04:19:12Z</time>
+</trkpt>
+<trkpt lat="-30.677428" lon="150.605564">
+<ele>0.000000</ele>
+<time>2004-04-24T04:19:43Z</time>
+</trkpt>
+<trkpt lat="-30.679901" lon="150.606111">
+<ele>0.000000</ele>
+<time>2004-04-24T04:20:13Z</time>
+</trkpt>
+<trkpt lat="-30.678442" lon="150.606111">
+<ele>0.000000</ele>
+<time>2004-04-24T04:20:44Z</time>
+</trkpt>
+<trkpt lat="-30.677975" lon="150.607045">
+<ele>0.000000</ele>
+<time>2004-04-24T04:21:14Z</time>
+</trkpt>
+<trkpt lat="-30.677626" lon="150.606905">
+<ele>0.000000</ele>
+<time>2004-04-24T04:21:44Z</time>
+</trkpt>
+<trkpt lat="-30.679531" lon="150.605961">
+<ele>0.000000</ele>
+<time>2004-04-24T04:22:15Z</time>
+</trkpt>
+<trkpt lat="-30.678694" lon="150.607050">
+<ele>0.000000</ele>
+<time>2004-04-24T04:22:45Z</time>
+</trkpt>
+<trkpt lat="-30.678624" lon="150.607619">
+<ele>0.000000</ele>
+<time>2004-04-24T04:23:15Z</time>
+</trkpt>
+<trkpt lat="-30.678329" lon="150.607399">
+<ele>0.000000</ele>
+<time>2004-04-24T04:23:45Z</time>
+</trkpt>
+<trkpt lat="-30.678147" lon="150.607624">
+<ele>0.000000</ele>
+<time>2004-04-24T04:24:16Z</time>
+</trkpt>
+<trkpt lat="-30.679225" lon="150.608101">
+<ele>0.000000</ele>
+<time>2004-04-24T04:24:47Z</time>
+</trkpt>
+<trkpt lat="-30.679171" lon="150.605484">
+<ele>0.000000</ele>
+<time>2004-04-24T04:25:18Z</time>
+</trkpt>
+<trkpt lat="-30.679906" lon="150.604003">
+<ele>0.000000</ele>
+<time>2004-04-24T04:25:48Z</time>
+</trkpt>
+<trkpt lat="-30.678136" lon="150.605881">
+<ele>0.000000</ele>
+<time>2004-04-24T04:26:19Z</time>
+</trkpt>
+<trkpt lat="-30.677980" lon="150.606675">
+<ele>0.000000</ele>
+<time>2004-04-24T04:26:49Z</time>
+</trkpt>
+<trkpt lat="-30.679236" lon="150.605527">
+<ele>0.000000</ele>
+<time>2004-04-24T04:27:20Z</time>
+</trkpt>
+<trkpt lat="-30.678216" lon="150.605274">
+<ele>0.000000</ele>
+<time>2004-04-24T04:27:51Z</time>
+</trkpt>
+<trkpt lat="-30.681961" lon="150.604808">
+<ele>0.000000</ele>
+<time>2004-04-24T04:28:21Z</time>
+</trkpt>
+<trkpt lat="-30.686220" lon="150.605269">
+<ele>0.000000</ele>
+<time>2004-04-24T04:28:51Z</time>
+</trkpt>
+<trkpt lat="-30.691118" lon="150.606760">
+<ele>0.000000</ele>
+<time>2004-04-24T04:29:22Z</time>
+</trkpt>
+<trkpt lat="-30.695871" lon="150.609110">
+<ele>0.000000</ele>
+<time>2004-04-24T04:29:52Z</time>
+</trkpt>
+<trkpt lat="-30.700361" lon="150.611352">
+<ele>0.000000</ele>
+<time>2004-04-24T04:30:23Z</time>
+</trkpt>
+<trkpt lat="-30.704797" lon="150.615107">
+<ele>0.000000</ele>
+<time>2004-04-24T04:30:54Z</time>
+</trkpt>
+<trkpt lat="-30.705988" lon="150.617468">
+<ele>0.000000</ele>
+<time>2004-04-24T04:31:24Z</time>
+</trkpt>
+<trkpt lat="-30.707173" lon="150.619184">
+<ele>0.000000</ele>
+<time>2004-04-24T04:31:55Z</time>
+</trkpt>
+<trkpt lat="-30.705800" lon="150.620472">
+<ele>0.000000</ele>
+<time>2004-04-24T04:32:26Z</time>
+</trkpt>
+<trkpt lat="-30.706830" lon="150.620939">
+<ele>0.000000</ele>
+<time>2004-04-24T04:32:57Z</time>
+</trkpt>
+<trkpt lat="-30.710199" lon="150.620788">
+<ele>0.000000</ele>
+<time>2004-04-24T04:33:27Z</time>
+</trkpt>
+<trkpt lat="-30.714281" lon="150.622430">
+<ele>0.000000</ele>
+<time>2004-04-24T04:33:58Z</time>
+</trkpt>
+<trkpt lat="-30.717736" lon="150.627247">
+<ele>0.000000</ele>
+<time>2004-04-24T04:34:29Z</time>
+</trkpt>
+<trkpt lat="-30.719463" lon="150.630798">
+<ele>0.000000</ele>
+<time>2004-04-24T04:35:00Z</time>
+</trkpt>
+<trkpt lat="-30.719882" lon="150.633084">
+<ele>0.000000</ele>
+<time>2004-04-24T04:35:30Z</time>
+</trkpt>
+<trkpt lat="-30.719705" lon="150.630412">
+<ele>0.000000</ele>
+<time>2004-04-24T04:36:01Z</time>
+</trkpt>
+<trkpt lat="-30.719737" lon="150.631909">
+<ele>0.000000</ele>
+<time>2004-04-24T04:36:31Z</time>
+</trkpt>
+<trkpt lat="-30.718696" lon="150.633759">
+<ele>0.000000</ele>
+<time>2004-04-24T04:37:02Z</time>
+</trkpt>
+<trkpt lat="-30.718938" lon="150.633164">
+<ele>0.000000</ele>
+<time>2004-04-24T04:37:33Z</time>
+</trkpt>
+<trkpt lat="-30.718857" lon="150.634387">
+<ele>0.000000</ele>
+<time>2004-04-24T04:38:03Z</time>
+</trkpt>
+<trkpt lat="-30.720155" lon="150.633234">
+<ele>0.000000</ele>
+<time>2004-04-24T04:38:33Z</time>
+</trkpt>
+<trkpt lat="-30.724146" lon="150.635245">
+<ele>0.000000</ele>
+<time>2004-04-24T04:39:04Z</time>
+</trkpt>
+<trkpt lat="-30.727816" lon="150.639730">
+<ele>0.000000</ele>
+<time>2004-04-24T04:39:35Z</time>
+</trkpt>
+<trkpt lat="-30.729768" lon="150.645792">
+<ele>0.000000</ele>
+<time>2004-04-24T04:40:05Z</time>
+</trkpt>
+<trkpt lat="-30.729050" lon="150.647487">
+<ele>0.000000</ele>
+<time>2004-04-24T04:40:35Z</time>
+</trkpt>
+<trkpt lat="-30.729548" lon="150.647407">
+<ele>0.000000</ele>
+<time>2004-04-24T04:41:05Z</time>
+</trkpt>
+<trkpt lat="-30.730063" lon="150.648855">
+<ele>0.000000</ele>
+<time>2004-04-24T04:41:35Z</time>
+</trkpt>
+<trkpt lat="-30.729629" lon="150.650856">
+<ele>0.000000</ele>
+<time>2004-04-24T04:42:05Z</time>
+</trkpt>
+<trkpt lat="-30.730257" lon="150.647643">
+<ele>0.000000</ele>
+<time>2004-04-24T04:42:36Z</time>
+</trkpt>
+<trkpt lat="-30.730739" lon="150.648855">
+<ele>0.000000</ele>
+<time>2004-04-24T04:43:07Z</time>
+</trkpt>
+<trkpt lat="-30.731431" lon="150.648785">
+<ele>0.000000</ele>
+<time>2004-04-24T04:43:38Z</time>
+</trkpt>
+<trkpt lat="-30.730750" lon="150.649112">
+<ele>0.000000</ele>
+<time>2004-04-24T04:44:09Z</time>
+</trkpt>
+<trkpt lat="-30.731732" lon="150.647696">
+<ele>0.000000</ele>
+<time>2004-04-24T04:44:40Z</time>
+</trkpt>
+<trkpt lat="-30.732236" lon="150.645926">
+<ele>0.000000</ele>
+<time>2004-04-24T04:45:11Z</time>
+</trkpt>
+<trkpt lat="-30.734875" lon="150.646226">
+<ele>0.000000</ele>
+<time>2004-04-24T04:45:42Z</time>
+</trkpt>
+<trkpt lat="-30.735836" lon="150.652401">
+<ele>0.000000</ele>
+<time>2004-04-24T04:46:13Z</time>
+</trkpt>
+<trkpt lat="-30.739505" lon="150.654139">
+<ele>0.000000</ele>
+<time>2004-04-24T04:46:44Z</time>
+</trkpt>
+<trkpt lat="-30.743238" lon="150.656923">
+<ele>0.000000</ele>
+<time>2004-04-24T04:47:15Z</time>
+</trkpt>
+<trkpt lat="-30.746725" lon="150.661901">
+<ele>0.000000</ele>
+<time>2004-04-24T04:47:46Z</time>
+</trkpt>
+<trkpt lat="-30.749381" lon="150.667073">
+<ele>0.000000</ele>
+<time>2004-04-24T04:48:16Z</time>
+</trkpt>
+<trkpt lat="-30.749708" lon="150.673853">
+<ele>0.000000</ele>
+<time>2004-04-24T04:48:47Z</time>
+</trkpt>
+<trkpt lat="-30.749847" lon="150.676219">
+<ele>0.000000</ele>
+<time>2004-04-24T04:49:18Z</time>
+</trkpt>
+<trkpt lat="-30.752562" lon="150.680140">
+<ele>0.000000</ele>
+<time>2004-04-24T04:49:48Z</time>
+</trkpt>
+<trkpt lat="-30.755705" lon="150.684925">
+<ele>0.000000</ele>
+<time>2004-04-24T04:50:19Z</time>
+</trkpt>
+<trkpt lat="-30.758066" lon="150.689721">
+<ele>0.000000</ele>
+<time>2004-04-24T04:50:49Z</time>
+</trkpt>
+<trkpt lat="-30.759916" lon="150.696180">
+<ele>0.000000</ele>
+<time>2004-04-24T04:51:19Z</time>
+</trkpt>
+<trkpt lat="-30.762395" lon="150.703411">
+<ele>0.000000</ele>
+<time>2004-04-24T04:51:50Z</time>
+</trkpt>
+<trkpt lat="-30.764074" lon="150.710707">
+<ele>0.000000</ele>
+<time>2004-04-24T04:52:21Z</time>
+</trkpt>
+<trkpt lat="-30.765592" lon="150.715862">
+<ele>0.000000</ele>
+<time>2004-04-24T04:52:51Z</time>
+</trkpt>
+<trkpt lat="-30.763983" lon="150.721924">
+<ele>0.000000</ele>
+<time>2004-04-24T04:53:21Z</time>
+</trkpt>
+<trkpt lat="-30.765254" lon="150.725979">
+<ele>0.000000</ele>
+<time>2004-04-24T04:53:52Z</time>
+</trkpt>
+<trkpt lat="-30.765292" lon="150.723812">
+<ele>0.000000</ele>
+<time>2004-04-24T04:54:23Z</time>
+</trkpt>
+<trkpt lat="-30.765544" lon="150.725657">
+<ele>0.000000</ele>
+<time>2004-04-24T04:54:53Z</time>
+</trkpt>
+<trkpt lat="-30.763623" lon="150.724289">
+<ele>0.000000</ele>
+<time>2004-04-24T04:55:24Z</time>
+</trkpt>
+<trkpt lat="-30.763720" lon="150.723968">
+<ele>0.000000</ele>
+<time>2004-04-24T04:55:54Z</time>
+</trkpt>
+<trkpt lat="-30.763843" lon="150.723705">
+<ele>0.000000</ele>
+<time>2004-04-24T04:56:25Z</time>
+</trkpt>
+<trkpt lat="-30.764090" lon="150.723565">
+<ele>0.000000</ele>
+<time>2004-04-24T04:56:56Z</time>
+</trkpt>
+<trkpt lat="-30.764138" lon="150.723399">
+<ele>0.000000</ele>
+<time>2004-04-24T04:57:19Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<trk>
+ <name> #4</name>
+<number>3</number>
+<trkseg>
+<trkpt lat="-30.677283" lon="150.598301">
+<ele>0.000000</ele>
+<time>2004-04-25T03:48:36Z</time>
+</trkpt>
+<trkpt lat="-30.675572" lon="150.605253">
+<ele>0.000000</ele>
+<time>2004-04-25T03:49:08Z</time>
+</trkpt>
+<trkpt lat="-30.675142" lon="150.605425">
+<ele>0.000000</ele>
+<time>2004-04-25T03:49:39Z</time>
+</trkpt>
+<trkpt lat="-30.674467" lon="150.605902">
+<ele>0.000000</ele>
+<time>2004-04-25T03:50:09Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<rte>
+ <name>AB1</name>
+ <rtept lat="-30.675867" lon="150.609035">
+ <ele>0.000000</ele>
+ <name>BORAH </name>
+ </rtept>
+ <rtept lat="-30.729929" lon="150.649193">
+ <ele>0.000000</ele>
+ <name>BALDWI</name>
+ </rtept>
+ <rtept lat="-30.763677" lon="150.723195">
+ <ele>0.000000</ele>
+ <name>MANAIR</name>
+ </rtept>
+</rte>
+</gpx>
--- /dev/null
+<?xml version="1.0"?>
+<gpx
+ version="1.0"
+creator="GPSBabel - http://gpsbabel.sourceforge.net"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xmlns="http://www.topografix.com/GPX/1/0"
+xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
+<time>1970-01-01T00:00:00Z</time>
+<trk>
+ <name>GNSSALTTRK</name>
+ <desc>IGCHDRS~HFPLTPILOT:CHRIS JONES~</desc>
+<trkseg>
+<trkpt lat="-30.676317" lon="150.606400">
+<time>2004-04-24T04:00:19Z</time>
+</trkpt>
+<trkpt lat="-30.676900" lon="150.605283">
+<time>2004-04-24T04:00:49Z</time>
+</trkpt>
+<trkpt lat="-30.678317" lon="150.605433">
+<time>2004-04-24T04:01:19Z</time>
+</trkpt>
+<trkpt lat="-30.677150" lon="150.607183">
+<time>2004-04-24T04:01:50Z</time>
+</trkpt>
+<trkpt lat="-30.677133" lon="150.609417">
+<time>2004-04-24T04:02:20Z</time>
+</trkpt>
+<trkpt lat="-30.678150" lon="150.607183">
+<time>2004-04-24T04:02:51Z</time>
+</trkpt>
+<trkpt lat="-30.675917" lon="150.604500">
+<time>2004-04-24T04:03:22Z</time>
+</trkpt>
+<trkpt lat="-30.678667" lon="150.605933">
+<time>2004-04-24T04:03:52Z</time>
+</trkpt>
+<trkpt lat="-30.681400" lon="150.607533">
+<time>2004-04-24T04:04:23Z</time>
+</trkpt>
+<trkpt lat="-30.677633" lon="150.606983">
+<time>2004-04-24T04:04:54Z</time>
+</trkpt>
+<trkpt lat="-30.680183" lon="150.608450">
+<time>2004-04-24T04:05:24Z</time>
+</trkpt>
+<trkpt lat="-30.678133" lon="150.606300">
+<time>2004-04-24T04:05:55Z</time>
+</trkpt>
+<trkpt lat="-30.678567" lon="150.606817">
+<time>2004-04-24T04:06:26Z</time>
+</trkpt>
+<trkpt lat="-30.678100" lon="150.606517">
+<time>2004-04-24T04:06:57Z</time>
+</trkpt>
+<trkpt lat="-30.678800" lon="150.606983">
+<time>2004-04-24T04:07:28Z</time>
+</trkpt>
+<trkpt lat="-30.677600" lon="150.606100">
+<time>2004-04-24T04:07:58Z</time>
+</trkpt>
+<trkpt lat="-30.679750" lon="150.607450">
+<time>2004-04-24T04:08:29Z</time>
+</trkpt>
+<trkpt lat="-30.680367" lon="150.607217">
+<time>2004-04-24T04:09:00Z</time>
+</trkpt>
+<trkpt lat="-30.679917" lon="150.608917">
+<time>2004-04-24T04:09:31Z</time>
+</trkpt>
+<trkpt lat="-30.680150" lon="150.609550">
+<time>2004-04-24T04:10:01Z</time>
+</trkpt>
+<trkpt lat="-30.680317" lon="150.607400">
+<time>2004-04-24T04:10:32Z</time>
+</trkpt>
+<trkpt lat="-30.679750" lon="150.605067">
+<time>2004-04-24T04:11:02Z</time>
+</trkpt>
+<trkpt lat="-30.680267" lon="150.605450">
+<time>2004-04-24T04:11:32Z</time>
+</trkpt>
+<trkpt lat="-30.680767" lon="150.606617">
+<time>2004-04-24T04:12:03Z</time>
+</trkpt>
+<trkpt lat="-30.680217" lon="150.608383">
+<time>2004-04-24T04:12:34Z</time>
+</trkpt>
+<trkpt lat="-30.679333" lon="150.608467">
+<time>2004-04-24T04:13:04Z</time>
+</trkpt>
+<trkpt lat="-30.678233" lon="150.605317">
+<time>2004-04-24T04:13:35Z</time>
+</trkpt>
+<trkpt lat="-30.675633" lon="150.602533">
+<time>2004-04-24T04:14:06Z</time>
+</trkpt>
+<trkpt lat="-30.678250" lon="150.605133">
+<time>2004-04-24T04:14:37Z</time>
+</trkpt>
+<trkpt lat="-30.679300" lon="150.607017">
+<time>2004-04-24T04:15:07Z</time>
+</trkpt>
+<trkpt lat="-30.679433" lon="150.608167">
+<time>2004-04-24T04:15:38Z</time>
+</trkpt>
+<trkpt lat="-30.679217" lon="150.608217">
+<time>2004-04-24T04:16:08Z</time>
+</trkpt>
+<trkpt lat="-30.678983" lon="150.608233">
+<time>2004-04-24T04:16:39Z</time>
+</trkpt>
+<trkpt lat="-30.679167" lon="150.608783">
+<time>2004-04-24T04:17:10Z</time>
+</trkpt>
+<trkpt lat="-30.680317" lon="150.606550">
+<time>2004-04-24T04:17:41Z</time>
+</trkpt>
+<trkpt lat="-30.680100" lon="150.605867">
+<time>2004-04-24T04:18:11Z</time>
+</trkpt>
+<trkpt lat="-30.676150" lon="150.605733">
+<time>2004-04-24T04:18:41Z</time>
+</trkpt>
+<trkpt lat="-30.675433" lon="150.604217">
+<time>2004-04-24T04:19:12Z</time>
+</trkpt>
+<trkpt lat="-30.677433" lon="150.605567">
+<time>2004-04-24T04:19:43Z</time>
+</trkpt>
+<trkpt lat="-30.679900" lon="150.606117">
+<time>2004-04-24T04:20:13Z</time>
+</trkpt>
+<trkpt lat="-30.678450" lon="150.606117">
+<time>2004-04-24T04:20:44Z</time>
+</trkpt>
+<trkpt lat="-30.677967" lon="150.607050">
+<time>2004-04-24T04:21:14Z</time>
+</trkpt>
+<trkpt lat="-30.677633" lon="150.606900">
+<time>2004-04-24T04:21:44Z</time>
+</trkpt>
+<trkpt lat="-30.679533" lon="150.605967">
+<time>2004-04-24T04:22:15Z</time>
+</trkpt>
+<trkpt lat="-30.678700" lon="150.607050">
+<time>2004-04-24T04:22:45Z</time>
+</trkpt>
+<trkpt lat="-30.678617" lon="150.607617">
+<time>2004-04-24T04:23:15Z</time>
+</trkpt>
+<trkpt lat="-30.678333" lon="150.607400">
+<time>2004-04-24T04:23:45Z</time>
+</trkpt>
+<trkpt lat="-30.678150" lon="150.607617">
+<time>2004-04-24T04:24:16Z</time>
+</trkpt>
+<trkpt lat="-30.679217" lon="150.608100">
+<time>2004-04-24T04:24:47Z</time>
+</trkpt>
+<trkpt lat="-30.679167" lon="150.605483">
+<time>2004-04-24T04:25:18Z</time>
+</trkpt>
+<trkpt lat="-30.679900" lon="150.604000">
+<time>2004-04-24T04:25:48Z</time>
+</trkpt>
+<trkpt lat="-30.678133" lon="150.605883">
+<time>2004-04-24T04:26:19Z</time>
+</trkpt>
+<trkpt lat="-30.677983" lon="150.606667">
+<time>2004-04-24T04:26:49Z</time>
+</trkpt>
+<trkpt lat="-30.679233" lon="150.605533">
+<time>2004-04-24T04:27:20Z</time>
+</trkpt>
+<trkpt lat="-30.678217" lon="150.605267">
+<time>2004-04-24T04:27:51Z</time>
+</trkpt>
+<trkpt lat="-30.681967" lon="150.604800">
+<time>2004-04-24T04:28:21Z</time>
+</trkpt>
+<trkpt lat="-30.686217" lon="150.605267">
+<time>2004-04-24T04:28:51Z</time>
+</trkpt>
+<trkpt lat="-30.691117" lon="150.606767">
+<time>2004-04-24T04:29:22Z</time>
+</trkpt>
+<trkpt lat="-30.695867" lon="150.609117">
+<time>2004-04-24T04:29:52Z</time>
+</trkpt>
+<trkpt lat="-30.700367" lon="150.611350">
+<time>2004-04-24T04:30:23Z</time>
+</trkpt>
+<trkpt lat="-30.704800" lon="150.615100">
+<time>2004-04-24T04:30:54Z</time>
+</trkpt>
+<trkpt lat="-30.705983" lon="150.617467">
+<time>2004-04-24T04:31:24Z</time>
+</trkpt>
+<trkpt lat="-30.707167" lon="150.619183">
+<time>2004-04-24T04:31:55Z</time>
+</trkpt>
+<trkpt lat="-30.705800" lon="150.620467">
+<time>2004-04-24T04:32:26Z</time>
+</trkpt>
+<trkpt lat="-30.706833" lon="150.620933">
+<time>2004-04-24T04:32:57Z</time>
+</trkpt>
+<trkpt lat="-30.710200" lon="150.620783">
+<time>2004-04-24T04:33:27Z</time>
+</trkpt>
+<trkpt lat="-30.714283" lon="150.622433">
+<time>2004-04-24T04:33:58Z</time>
+</trkpt>
+<trkpt lat="-30.717733" lon="150.627250">
+<time>2004-04-24T04:34:29Z</time>
+</trkpt>
+<trkpt lat="-30.719467" lon="150.630800">
+<time>2004-04-24T04:35:00Z</time>
+</trkpt>
+<trkpt lat="-30.719883" lon="150.633083">
+<time>2004-04-24T04:35:30Z</time>
+</trkpt>
+<trkpt lat="-30.719700" lon="150.630417">
+<time>2004-04-24T04:36:01Z</time>
+</trkpt>
+<trkpt lat="-30.719733" lon="150.631917">
+<time>2004-04-24T04:36:31Z</time>
+</trkpt>
+<trkpt lat="-30.718700" lon="150.633767">
+<time>2004-04-24T04:37:02Z</time>
+</trkpt>
+<trkpt lat="-30.718933" lon="150.633167">
+<time>2004-04-24T04:37:33Z</time>
+</trkpt>
+<trkpt lat="-30.718850" lon="150.634383">
+<time>2004-04-24T04:38:03Z</time>
+</trkpt>
+<trkpt lat="-30.720150" lon="150.633233">
+<time>2004-04-24T04:38:33Z</time>
+</trkpt>
+<trkpt lat="-30.724150" lon="150.635250">
+<time>2004-04-24T04:39:04Z</time>
+</trkpt>
+<trkpt lat="-30.727817" lon="150.639733">
+<time>2004-04-24T04:39:35Z</time>
+</trkpt>
+<trkpt lat="-30.729767" lon="150.645800">
+<time>2004-04-24T04:40:05Z</time>
+</trkpt>
+<trkpt lat="-30.729050" lon="150.647483">
+<time>2004-04-24T04:40:35Z</time>
+</trkpt>
+<trkpt lat="-30.729550" lon="150.647400">
+<time>2004-04-24T04:41:05Z</time>
+</trkpt>
+<trkpt lat="-30.730067" lon="150.648850">
+<time>2004-04-24T04:41:35Z</time>
+</trkpt>
+<trkpt lat="-30.729633" lon="150.650850">
+<time>2004-04-24T04:42:05Z</time>
+</trkpt>
+<trkpt lat="-30.730250" lon="150.647650">
+<time>2004-04-24T04:42:36Z</time>
+</trkpt>
+<trkpt lat="-30.730733" lon="150.648850">
+<time>2004-04-24T04:43:07Z</time>
+</trkpt>
+<trkpt lat="-30.731433" lon="150.648783">
+<time>2004-04-24T04:43:38Z</time>
+</trkpt>
+<trkpt lat="-30.730750" lon="150.649117">
+<time>2004-04-24T04:44:09Z</time>
+</trkpt>
+<trkpt lat="-30.731733" lon="150.647700">
+<time>2004-04-24T04:44:40Z</time>
+</trkpt>
+<trkpt lat="-30.732233" lon="150.645933">
+<time>2004-04-24T04:45:11Z</time>
+</trkpt>
+<trkpt lat="-30.734867" lon="150.646233">
+<time>2004-04-24T04:45:42Z</time>
+</trkpt>
+<trkpt lat="-30.735833" lon="150.652400">
+<time>2004-04-24T04:46:13Z</time>
+</trkpt>
+<trkpt lat="-30.739500" lon="150.654133">
+<time>2004-04-24T04:46:44Z</time>
+</trkpt>
+<trkpt lat="-30.743233" lon="150.656917">
+<time>2004-04-24T04:47:15Z</time>
+</trkpt>
+<trkpt lat="-30.746733" lon="150.661900">
+<time>2004-04-24T04:47:46Z</time>
+</trkpt>
+<trkpt lat="-30.749383" lon="150.667067">
+<time>2004-04-24T04:48:16Z</time>
+</trkpt>
+<trkpt lat="-30.749700" lon="150.673850">
+<time>2004-04-24T04:48:47Z</time>
+</trkpt>
+<trkpt lat="-30.749850" lon="150.676217">
+<time>2004-04-24T04:49:18Z</time>
+</trkpt>
+<trkpt lat="-30.752567" lon="150.680133">
+<time>2004-04-24T04:49:48Z</time>
+</trkpt>
+<trkpt lat="-30.755700" lon="150.684917">
+<time>2004-04-24T04:50:19Z</time>
+</trkpt>
+<trkpt lat="-30.758067" lon="150.689717">
+<time>2004-04-24T04:50:49Z</time>
+</trkpt>
+<trkpt lat="-30.759917" lon="150.696183">
+<time>2004-04-24T04:51:19Z</time>
+</trkpt>
+<trkpt lat="-30.762400" lon="150.703417">
+<time>2004-04-24T04:51:50Z</time>
+</trkpt>
+<trkpt lat="-30.764067" lon="150.710700">
+<time>2004-04-24T04:52:21Z</time>
+</trkpt>
+<trkpt lat="-30.765600" lon="150.715867">
+<time>2004-04-24T04:52:51Z</time>
+</trkpt>
+<trkpt lat="-30.763983" lon="150.721917">
+<time>2004-04-24T04:53:21Z</time>
+</trkpt>
+<trkpt lat="-30.765250" lon="150.725983">
+<time>2004-04-24T04:53:52Z</time>
+</trkpt>
+<trkpt lat="-30.765300" lon="150.723817">
+<time>2004-04-24T04:54:23Z</time>
+</trkpt>
+<trkpt lat="-30.765550" lon="150.725650">
+<time>2004-04-24T04:54:53Z</time>
+</trkpt>
+<trkpt lat="-30.763617" lon="150.724283">
+<time>2004-04-24T04:55:24Z</time>
+</trkpt>
+<trkpt lat="-30.763717" lon="150.723967">
+<time>2004-04-24T04:55:54Z</time>
+</trkpt>
+<trkpt lat="-30.763850" lon="150.723700">
+<time>2004-04-24T04:56:25Z</time>
+</trkpt>
+<trkpt lat="-30.764083" lon="150.723567">
+<time>2004-04-24T04:56:56Z</time>
+</trkpt>
+<trkpt lat="-30.764133" lon="150.723400">
+<time>2004-04-24T04:57:19Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<rte>
+ <name>0001</name>
+ <desc>IGCDATE000000: </desc>
+ <rtept lat="-30.675867" lon="150.609033">
+ <name>TAKEOFF</name>
+ <cmt>BORAH </cmt>
+ </rtept>
+ <rtept lat="-30.675867" lon="150.609033">
+ <name>START</name>
+ <cmt>BORAH </cmt>
+ </rtept>
+ <rtept lat="-30.729933" lon="150.649200">
+ <name>TURN01</name>
+ <cmt>BALDWI</cmt>
+ </rtept>
+ <rtept lat="-30.763683" lon="150.723200">
+ <name>FINISH</name>
+ <cmt>MANAIR</cmt>
+ </rtept>
+ <rtept lat="-30.763683" lon="150.723200">
+ <name>LANDING</name>
+ <cmt>MANAIR</cmt>
+ </rtept>
+</rte>
+</gpx>
--- /dev/null
+AXXXZZZGPSBabel\r
+HFDTE240404\r
+HFPLTPILOT:CHRIS JONES\r
+C010170000000000000000101\r
+C3040552S15036542EBORAH \r
+C3040552S15036542EBORAH \r
+C3043796S15038952EBALDWI\r
+C3045821S15043392EMANAIR\r
+C3045821S15043392EMANAIR\r
+B0400193040579S15036384EA0000000000\r
+B0400493040614S15036317EA0000000000\r
+B0401193040699S15036326EA0000000000\r
+B0401503040629S15036431EA0000000000\r
+B0402203040628S15036565EA0000000000\r
+B0402513040689S15036431EA0000000000\r
+B0403223040555S15036270EA0000000000\r
+B0403523040720S15036356EA0000000000\r
+B0404233040884S15036452EA0000000000\r
+B0404543040658S15036419EA0000000000\r
+B0405243040811S15036507EA0000000000\r
+B0405553040688S15036378EA0000000000\r
+B0406263040714S15036409EA0000000000\r
+B0406573040686S15036391EA0000000000\r
+B0407283040728S15036419EA0000000000\r
+B0407583040656S15036366EA0000000000\r
+B0408293040785S15036447EA0000000000\r
+B0409003040822S15036433EA0000000000\r
+B0409313040795S15036535EA0000000000\r
+B0410013040809S15036573EA0000000000\r
+B0410323040819S15036444EA0000000000\r
+B0411023040785S15036304EA0000000000\r
+B0411323040816S15036327EA0000000000\r
+B0412033040846S15036397EA0000000000\r
+B0412343040813S15036503EA0000000000\r
+B0413043040760S15036508EA0000000000\r
+B0413353040694S15036319EA0000000000\r
+B0414063040538S15036152EA0000000000\r
+B0414373040695S15036308EA0000000000\r
+B0415073040758S15036421EA0000000000\r
+B0415383040766S15036490EA0000000000\r
+B0416083040753S15036493EA0000000000\r
+B0416393040739S15036494EA0000000000\r
+B0417103040750S15036527EA0000000000\r
+B0417413040819S15036393EA0000000000\r
+B0418113040806S15036352EA0000000000\r
+B0418413040569S15036344EA0000000000\r
+B0419123040526S15036253EA0000000000\r
+B0419433040646S15036334EA0000000000\r
+B0420133040794S15036367EA0000000000\r
+B0420443040707S15036367EA0000000000\r
+B0421143040678S15036423EA0000000000\r
+B0421443040658S15036414EA0000000000\r
+B0422153040772S15036358EA0000000000\r
+B0422453040722S15036423EA0000000000\r
+B0423153040717S15036457EA0000000000\r
+B0423453040700S15036444EA0000000000\r
+B0424163040689S15036457EA0000000000\r
+B0424473040753S15036486EA0000000000\r
+B0425183040750S15036329EA0000000000\r
+B0425483040794S15036240EA0000000000\r
+B0426193040688S15036353EA0000000000\r
+B0426493040679S15036400EA0000000000\r
+B0427203040754S15036332EA0000000000\r
+B0427513040693S15036316EA0000000000\r
+B0428213040918S15036288EA0000000000\r
+B0428513041173S15036316EA0000000000\r
+B0429223041467S15036406EA0000000000\r
+B0429523041752S15036547EA0000000000\r
+B0430233042022S15036681EA0000000000\r
+B0430543042288S15036906EA0000000000\r
+B0431243042359S15037048EA0000000000\r
+B0431553042430S15037151EA0000000000\r
+B0432263042348S15037228EA0000000000\r
+B0432573042410S15037256EA0000000000\r
+B0433273042612S15037247EA0000000000\r
+B0433583042857S15037346EA0000000000\r
+B0434293043064S15037635EA0000000000\r
+B0435003043168S15037848EA0000000000\r
+B0435303043193S15037985EA0000000000\r
+B0436013043182S15037825EA0000000000\r
+B0436313043184S15037915EA0000000000\r
+B0437023043122S15038026EA0000000000\r
+B0437333043136S15037990EA0000000000\r
+B0438033043131S15038063EA0000000000\r
+B0438333043209S15037994EA0000000000\r
+B0439043043449S15038115EA0000000000\r
+B0439353043669S15038384EA0000000000\r
+B0440053043786S15038748EA0000000000\r
+B0440353043743S15038849EA0000000000\r
+B0441053043773S15038844EA0000000000\r
+B0441353043804S15038931EA0000000000\r
+B0442053043778S15039051EA0000000000\r
+B0442363043815S15038859EA0000000000\r
+B0443073043844S15038931EA0000000000\r
+B0443383043886S15038927EA0000000000\r
+B0444093043845S15038947EA0000000000\r
+B0444403043904S15038862EA0000000000\r
+B0445113043934S15038756EA0000000000\r
+B0445423044092S15038774EA0000000000\r
+B0446133044150S15039144EA0000000000\r
+B0446443044370S15039248EA0000000000\r
+B0447153044594S15039415EA0000000000\r
+B0447463044804S15039714EA0000000000\r
+B0448163044963S15040024EA0000000000\r
+B0448473044982S15040431EA0000000000\r
+B0449183044991S15040573EA0000000000\r
+B0449483045154S15040808EA0000000000\r
+B0450193045342S15041095EA0000000000\r
+B0450493045484S15041383EA0000000000\r
+B0451193045595S15041771EA0000000000\r
+B0451503045744S15042205EA0000000000\r
+B0452213045844S15042642EA0000000000\r
+B0452513045936S15042952EA0000000000\r
+B0453213045839S15043315EA0000000000\r
+B0453523045915S15043559EA0000000000\r
+B0454233045918S15043429EA0000000000\r
+B0454533045933S15043539EA0000000000\r
+B0455243045817S15043457EA0000000000\r
+B0455543045823S15043438EA0000000000\r
+B0456253045831S15043422EA0000000000\r
+B0456563045845S15043414EA0000000000\r
+B0457193045848S15043404EA0000000000\r
+GGPSBabelSecurityRecordGuaranteedToFailVALIChecks\r
--- /dev/null
+AXXXABCFLIGHT:1\r
+HFFXA035\r
+HFDTE160701\r
+HFPLTPILOT:Bill Bloggs\r
+HFGTYGLIDERTYPE:Schleicher ASH-25\r
+HFGIDGLIDERID:ABCD-1234\r
+HFDTM100GPSDATUM:WGS-1984\r
+HFRFWFIRMWAREVERSION:6.4\r
+HFRHWHARDWAREVERSION:3.0\r
+HFFTYFRTYPE:Manufacturer Model\r
+HFGPSMarconiCanada:Superstar 12ch max10000m\r
+HFPRSPRESSALTSENSOR:Sensyn XYZ1111 max11000m\r
+HFCIDCOMPETITIONID:XYZ-78910\r
+HFCCLCOMPETITIONCLASS:15mMotor Glider\r
+HFSCMSECONDCREW:JohnSmith\r
+I023638FXA3941ENL\r
+J010812HDT\r
+C150701213841160701000102500KTri\r
+C5111359N00101899WLashamClubhouse\r
+C5110179N00102644WLashamStart S Start\r
+C5209092N00255227WSarnesfield TP1\r
+C5230147N00017612WNormanCross TP2\r
+C5110179N00102644WLashamStart S Finish\r
+C5111359N00101899WLashamClubhouse\r
+F160240040609123624221821\r
+B1602405407121N00249342WA0028000421020509950\r
+D20331\r
+E160245PEV\r
+B1602455107126N00149300WA0028800429019509020\r
+B1602505107134N00149283WA0029000432021009015\r
+B1602555107140N00149221WA0029000430020009012\r
+F1603000609123624221821\r
+B1603005107150N00149202WA0029100432025608009\r
+E160305PEV\r
+B1603055107180N00149185WA0029100435021008015\r
+B1603105107212N00149174WA0029300435019608024\r
+K160248090\r
+B1602485107220N00149150WA0049400436019008018\r
+B1602525107330N00149127WA0049600439019508015\r
+LXXXRURITANIANSTANDARD NATIONALS DAY 1\r
+LXXXFLIGHTTIME: 4:14:25 TASK SPEED:58.48KTS\r
+GREJNGJERJKNJKRE31895478537H43982FJN9248F942389T433T\r
+GJNJK1489IERGNV3089IVJE9GO398535J3894N358954983O0934\r
+GSKTO5427FGTNUT5621WKTC6714FT8957FGMKJ134527FGTR6751\r
+GK2489IERGNV3089IVJE39GO398535J3894N358954983FTGY546\r
+G12560DJUWT28719GTAOL5628FGWNIST78154INWTOLP7815FITN\r
--- /dev/null
+<?xml version="1.0"?>
+<gpx
+ version="1.0"
+creator="GPSBabel - http://gpsbabel.sourceforge.net"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xmlns="http://www.topografix.com/GPX/1/0"
+xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
+<time>1970-01-01T00:00:00Z</time>
+<trk>
+ <name>PRESALTTRK</name>
+ <desc>IGCHDRS~HFFXA035~HFPLTPILOT:Bill Bloggs~HFGTYGLIDERTYPE:Schleicher ASH-25~HFGIDGLIDERID:ABCD-1234~HFDTM100GPSDATUM:WGS-1984~HFRFWFIRMWAREVERSION:6.4~HFRHWHARDWAREVERSION:3.0~HFFTYFRTYPE:Manufacturer Model~HFGPSMarconiCanada:Superstar 12ch max10000m~HFPRSPRESSALTSENSOR:Sensyn XYZ1111 max11000m~HFCIDCOMPETITIONID:XYZ-78910~HFCCLCOMPETITIONCLASS:15mMotor Glider~HFSCMSECONDCREW:JohnSmith~</desc>
+<trkseg>
+<trkpt lat="54.118683" lon="-2.822367">
+<ele>280.000000</ele>
+<time>2001-07-16T16:02:40Z</time>
+</trkpt>
+<trkpt lat="51.118767" lon="-1.821667">
+<ele>288.000000</ele>
+<time>2001-07-16T16:02:45Z</time>
+</trkpt>
+<trkpt lat="51.118900" lon="-1.821383">
+<ele>290.000000</ele>
+<time>2001-07-16T16:02:50Z</time>
+</trkpt>
+<trkpt lat="51.119000" lon="-1.820350">
+<ele>290.000000</ele>
+<time>2001-07-16T16:02:55Z</time>
+</trkpt>
+<trkpt lat="51.119167" lon="-1.820033">
+<ele>291.000000</ele>
+<time>2001-07-16T16:03:00Z</time>
+</trkpt>
+<trkpt lat="51.119667" lon="-1.819750">
+<ele>291.000000</ele>
+<time>2001-07-16T16:03:05Z</time>
+</trkpt>
+<trkpt lat="51.120200" lon="-1.819567">
+<ele>293.000000</ele>
+<time>2001-07-16T16:03:10Z</time>
+</trkpt>
+<trkpt lat="51.120333" lon="-1.819167">
+<ele>494.000000</ele>
+<time>2001-07-17T16:02:48Z</time>
+</trkpt>
+<trkpt lat="51.122167" lon="-1.818783">
+<ele>496.000000</ele>
+<time>2001-07-17T16:02:52Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<trk>
+ <name>GNSSALTTRK</name>
+ <desc>IGCHDRS~HFFXA035~HFPLTPILOT:Bill Bloggs~HFGTYGLIDERTYPE:Schleicher ASH-25~HFGIDGLIDERID:ABCD-1234~HFDTM100GPSDATUM:WGS-1984~HFRFWFIRMWAREVERSION:6.4~HFRHWHARDWAREVERSION:3.0~HFFTYFRTYPE:Manufacturer Model~HFGPSMarconiCanada:Superstar 12ch max10000m~HFPRSPRESSALTSENSOR:Sensyn XYZ1111 max11000m~HFCIDCOMPETITIONID:XYZ-78910~HFCCLCOMPETITIONCLASS:15mMotor Glider~HFSCMSECONDCREW:JohnSmith~</desc>
+<trkseg>
+<trkpt lat="54.118683" lon="-2.822367">
+<ele>421.000000</ele>
+<time>2001-07-16T16:02:40Z</time>
+</trkpt>
+<trkpt lat="51.118767" lon="-1.821667">
+<ele>429.000000</ele>
+<time>2001-07-16T16:02:45Z</time>
+</trkpt>
+<trkpt lat="51.118900" lon="-1.821383">
+<ele>432.000000</ele>
+<time>2001-07-16T16:02:50Z</time>
+</trkpt>
+<trkpt lat="51.119000" lon="-1.820350">
+<ele>430.000000</ele>
+<time>2001-07-16T16:02:55Z</time>
+</trkpt>
+<trkpt lat="51.119167" lon="-1.820033">
+<ele>432.000000</ele>
+<time>2001-07-16T16:03:00Z</time>
+</trkpt>
+<trkpt lat="51.119667" lon="-1.819750">
+<ele>435.000000</ele>
+<time>2001-07-16T16:03:05Z</time>
+</trkpt>
+<trkpt lat="51.120200" lon="-1.819567">
+<ele>435.000000</ele>
+<time>2001-07-16T16:03:10Z</time>
+</trkpt>
+<trkpt lat="51.120333" lon="-1.819167">
+<ele>436.000000</ele>
+<time>2001-07-17T16:02:48Z</time>
+</trkpt>
+<trkpt lat="51.122167" lon="-1.818783">
+<ele>439.000000</ele>
+<time>2001-07-17T16:02:52Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<rte>
+ <name>0001</name>
+ <desc>IGCDATE160701: 500KTri</desc>
+ <rtept lat="51.189317" lon="-1.031650">
+<time>2001-07-15T21:38:41Z</time>
+ <name>TAKEOFF</name>
+ <cmt>LashamClubhouse</cmt>
+ </rtept>
+ <rtept lat="51.169650" lon="-1.044067">
+<time>2001-07-15T21:38:41Z</time>
+ <name>START</name>
+ <cmt>LashamStart S Start</cmt>
+ </rtept>
+ <rtept lat="52.151533" lon="-2.920450">
+<time>2001-07-15T21:38:41Z</time>
+ <name>TURN01</name>
+ <cmt>Sarnesfield TP1</cmt>
+ </rtept>
+ <rtept lat="52.502450" lon="-0.293533">
+<time>2001-07-15T21:38:41Z</time>
+ <name>TURN02</name>
+ <cmt>NormanCross TP2</cmt>
+ </rtept>
+ <rtept lat="51.169650" lon="-1.044067">
+<time>2001-07-15T21:38:41Z</time>
+ <name>FINISH</name>
+ <cmt>LashamStart S Finish</cmt>
+ </rtept>
+ <rtept lat="51.189317" lon="-1.031650">
+<time>2001-07-15T21:38:41Z</time>
+ <name>LANDING</name>
+ <cmt>LashamClubhouse</cmt>
+ </rtept>
+</rte>
+</gpx>
--- /dev/null
+AXXXZZZGPSBabel\r
+HFDTE160701\r
+HFFXA035\r
+HFPLTPILOT:Bill Bloggs\r
+HFGTYGLIDERTYPE:Schleicher ASH-25\r
+HFGIDGLIDERID:ABCD-1234\r
+HFDTM100GPSDATUM:WGS-1984\r
+HFRFWFIRMWAREVERSION:6.4\r
+HFRHWHARDWAREVERSION:3.0\r
+HFFTYFRTYPE:Manufacturer Model\r
+HFGPSMarconiCanada:Superstar 12ch max10000m\r
+HFPRSPRESSALTSENSOR:Sensyn XYZ1111 max11000m\r
+HFCIDCOMPETITIONID:XYZ-78910\r
+HFCCLCOMPETITIONCLASS:15mMotor Glider\r
+HFSCMSECONDCREW:JohnSmith\r
+C150701213841160701000102500KTri\r
+C5111359N00101899WLashamClubhouse\r
+C5110179N00102644WLashamStart S Start\r
+C5209092N00255227WSarnesfield TP1\r
+C5230147N00017612WNormanCross TP2\r
+C5110179N00102644WLashamStart S Finish\r
+C5111359N00101899WLashamClubhouse\r
+B1602405407121N00249342WA0028000421\r
+B1602455107126N00149300WA0028800429\r
+B1602505107134N00149283WA0029000432\r
+B1602555107140N00149221WA0029000430\r
+B1603005107150N00149202WA0029100432\r
+B1603055107180N00149185WA0029100435\r
+B1603105107212N00149174WA0029300435\r
+B1602485107220N00149150WA0049400436\r
+B1602525107330N00149127WA0049600439\r
+GGPSBabelSecurityRecordGuaranteedToFailVALIChecks\r
rte_head_ct++;
}
+void
+route_del_head(route_head *rte)
+{
+ dequeue( &rte->Q );
+ route_free( rte );
+ rte_head_ct--;
+}
+
void
track_add_head(route_head *rte)
{
trk_head_ct++;
}
+void
+track_del_head(route_head *rte)
+{
+ dequeue( &rte->Q );
+ route_free( rte );
+ trk_head_ct--;
+}
+
void
route_add_wpt(route_head *rte, waypoint *wpt)
{
${PNAME} -i netstumbler -f reference/netstumbler.txt -o mapsource -F ${TMPDIR}/netstumbler.mps
bincompare ${TMPDIR}/netstumbler.mps reference/netstumbler.mps
+#
+# IGC tests
+#
+rm -f ${TMPDIR}/igc*out
+${PNAME} -i gpx -f reference/igc1.gpx -o igc -F ${TMPDIR}/igc.out
+sed '/^LXXXGenerated by GPSBabel Version/d' ${TMPDIR}/igc.out > ${TMPDIR}/igc_sed.out
+compare ${TMPDIR}/igc_sed.out reference/igc1_igc.out
+
+${PNAME} -i igc -f ${TMPDIR}/igc.out -o gpx -F ${TMPDIR}/igc.gpx
+compare ${TMPDIR}/igc.gpx reference/igc1_gpx.out
+
+${PNAME} -i gpx -f ${TMPDIR}/igc.gpx -o igc -F ${TMPDIR}/igc.out
+sed '/^LXXXGenerated by GPSBabel Version/d' ${TMPDIR}/igc.out > ${TMPDIR}/igc_sed.out
+compare ${TMPDIR}/igc_sed.out reference/igc1_igc.out
+
+
+${PNAME} -i igc -f reference/igc2.igc -o gpx -F ${TMPDIR}/igc.gpx
+compare ${TMPDIR}/igc.gpx reference/igc2_gpx.out
+
+${PNAME} -i gpx -f ${TMPDIR}/igc.gpx -o igc -F ${TMPDIR}/igc.out
+sed '/^LXXXGenerated by GPSBabel Version/d' ${TMPDIR}/igc.out > ${TMPDIR}/igc_sed.out
+compare ${TMPDIR}/igc_sed.out reference/igc2_igc.out
+
+${PNAME} -i igc -f ${TMPDIR}/igc.out -o gpx -F ${TMPDIR}/igc.gpx
+compare ${TMPDIR}/igc.gpx reference/igc2_gpx.out
+
+
#
# XCSV "human readable" tests
#
return f;
}
+void
+xfprintf(const char *errtxt, FILE *stream, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ if (vfprintf(stream, format, ap) < 0) {
+ fatal("%s writing output file. Error was '%s'.\n",
+ errtxt, strerror(errno));
+ }
+ va_end(ap);
+}
+
+void
+xfputs(const char *errtxt, const char *s, FILE *stream)
+{
+ if (fputs(s, stream) < 0) {
+ fatal("%s Writing output file. Error was '%s'.\n",
+ errtxt, strerror(errno));
+ }
+}
/*
* Duplicate a pascal string into a normal C string.
extern ff_vecs_t html_vecs;
extern ff_vecs_t netstumbler_vecs;
extern ff_vecs_t HsaEndeavourNavigator_vecs;
+extern ff_vecs_t igc_vecs;
static
vecs_t vec_list[] = {
"HSA Endeavour Navigator export File",
NULL
},
+ {
+ &igc_vecs,
+ "igc",
+ "FAI/IGC Flight Recorder Data Format",
+ NULL
+ },
{
NULL,
NULL,